/***
*stdiostr.cpp -
*
*       Copyright (c) 1991-1997, Microsoft Corporation.  All rights reserved.
*
*Purpose:
*
*******************************************************************************/

#include <cruntime.h>
#include <internal.h>
#include <string.h>
#include <stdiostr.h>
#include <dbgint.h>
#pragma hdrstop

extern "C" {
#include <file2.h>
#include <msdos.h>
}
#include <dos.h>

        stdiobuf::stdiobuf(FILE * f)
: streambuf()
{
        unbuffered(1);                  // initially unbuffered
        _str = f;
}

        stdiobuf::~stdiobuf()
// : ~streambuf()
{
        stdiobuf::sync();               // make sure buffer flushed
}

        int stdiobuf::setrwbuf(int readsize, int writesize)
{
    char * tbuf;
    unbuffered(!(readsize+writesize));
    if (unbuffered())
        return(0);

    tbuf = _new_crt char[(readsize+writesize)];
    if (!tbuf)
        return(EOF);

    setb( tbuf, tbuf + (readsize+writesize), 1);

    if (readsize)
        {
        setg(base(),base()+readsize,base()+readsize);
        }
    else
        {
        setg(0,0,0);
        }

    if (writesize)
        {
        setp(base()+readsize,ebuf());
        }
    else
        {
        setp(0,0);
        }

    return(1);
}

int stdiobuf::overflow(int c) {
    long count, nout;
    if (allocate()==EOF)        // make sure there is a reserve area
        return EOF;
    if (!unbuffered() && epptr())
        {
        if ((count = pptr() - pbase()) > 0)
            {
            nout=fwrite((void *) pbase(), 1, (int)count, _str);
            pbump(-(int)nout);
            if (nout != count)
                {
                memmove(pbase(),pbase()+nout,(int)(count-nout));
                return(EOF);
                }
            }
        }
    if ((!unbuffered()) && (!epptr()))
        setp(base()+(blen()>>1),ebuf());
    if (c!=EOF)
        {
        if ((!unbuffered()) && (pptr() < epptr())) // guard against recursion
            sputc(c);
        else
            return fputc(c, _str);
        }
    return(1);  // return something other than EOF if successful
}

int stdiobuf::underflow()
{
    int count;
    if (allocate()==EOF)        // make sure there is a reserve area
        return EOF;
    if ((!unbuffered()) && (!egptr()))
        setg(base(),(base()+(blen()>>1)),(base()+(blen()>>1)));

    if (unbuffered() || (!egptr()))
        return fgetc(_str);
    if (gptr() >= egptr())
// buffer empty, try for more
    {
    if (!(count = fread((void *)eback(), 1, (size_t)(egptr()-eback()), _str)))
        return(EOF); // reach EOF, nothing read
    setg(eback(),(egptr()-count),egptr());   // _gptr = _egptr - count
    if (gptr()!=eback())
        {
        memmove(gptr(), eback(), count);        // overlapping memory!
        }
    }
    return sbumpc();
}

streampos stdiobuf::seekoff(streamoff off, ios::seek_dir dir, int)
{

    int fdir;
    long retpos;
    switch (dir) {
        case ios::beg :
            fdir = SEEK_SET;
            break;
        case ios::cur :
            fdir = SEEK_CUR;
            break;
        case ios::end :
            fdir = SEEK_END;
            break;
        default:
        // error
            return(EOF);
        }

    stdiobuf::overflow(EOF);
    if (fseek(_str, off, fdir))
        return (EOF);
    if ((retpos=ftell(_str))==-1L)
        return(EOF);
    return((streampos)retpos);
}

int stdiobuf::pbackfail(int c)
{
    if (eback()<gptr()) return sputbackc((char)c);

    if (stdiobuf::seekoff( -1, ios::cur, ios::in)==EOF)
        return EOF;
    if (!unbuffered() && egptr())
        {
        memmove((gptr()+1),gptr(),(egptr()-(gptr()+1)));
        *gptr()=(char)c;
        }
    return(c);
}

int stdiobuf::sync()
{
    long count;
    char * p;
    char flags;
    if (!unbuffered())
        {
        if (stdiobuf::overflow(EOF)==EOF)
            return(EOF);
        if ((count=in_avail())>0)
            {
#ifdef _WIN32
            flags = _osfile_safe(_fileno(_str));
#else  /* _WIN32 */
            flags = _osfile[_fileno(_str)];
#endif  /* _WIN32 */
            if (flags & FTEXT)
                {
                // If text mode, need to account for CR/LF etc.
                for (p = gptr(); p < egptr(); p++)
                    if (*p == '\n')
                        count++;

                // account for EOF if read, not counted by _read
                if (_str->_flag & _IOCTRLZ)
                    count++;
                }
            if (stdiobuf::seekoff( -count, ios::cur, ios::in)==EOF)
                return(EOF);

            setg(eback(),egptr(),egptr()); // empty get area (_gptr = _egptr;)
            }
        }
    return(0);
}

        stdiostream::stdiostream(FILE * file)
: iostream(_new_crt stdiobuf(file))
{
istream::delbuf(1);
ostream::delbuf(1);
}

        stdiostream::~stdiostream()
{
}

// include here for better granularity

int ios::sunk_with_stdio = 0;

void ios::sync_with_stdio()
{
    if (!sunk_with_stdio)       // first time only
        {
        cin = _new_crt stdiobuf(stdin);
        cin.delbuf(1);
        cin.setf(ios::stdio);

        cout = _new_crt stdiobuf(stdout);
        cout.delbuf(1);
        cout.setf(ios::stdio|ios::unitbuf);
        ((stdiobuf*)(cout.rdbuf()))->setrwbuf(0,80);

        cerr = _new_crt stdiobuf(stderr);
        cerr.delbuf(1);
        cerr.setf(ios::stdio|ios::unitbuf);
        ((stdiobuf*)(cerr.rdbuf()))->setrwbuf(0,80);

        clog = _new_crt stdiobuf(stderr);
        clog.delbuf(1);
        clog.setf(ios::stdio);
        ((stdiobuf*)(clog.rdbuf()))->setrwbuf(0,BUFSIZ);

        sunk_with_stdio++;
        }
}
